﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using Logger;
using Adler32Checksum;
using System.Diagnostics.Contracts;

namespace RaidAnything
{
    static class Program
    {

        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main()
        {
            ManualTesting mt = new ManualTesting();
            mt.Main();
        }
    }

    internal class ManualTesting
    {
        private Log log;
        private System.IO.TextWriter output;
        private System.IO.TextReader input;
        private readonly String sampleText;

        public ManualTesting()
        {
            log = Log.Create("Program");
            output = System.Console.Out;
            input = System.Console.In;

            Contract.Assume(output != null);
            Contract.Assume(input != null);
            sampleText = "Hello World. Nervously I loaded the twin ducks aboard the revolving platform.";

        }

        [ContractInvariantMethod]
        private void ObjectInvariant()
        {
            Contract.Invariant(log != null);
            Contract.Invariant(output != null);
            Contract.Invariant(input != null);
            Contract.Invariant(sampleText != null);
            Contract.Invariant(sampleText.Length > 4);
        }

        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        public void Main()
        {
            System.Threading.Thread.Sleep(1000);
            ConsoleLogger.Init();
            ConsoleLogger.BlackListFilter("Berlekamp");

            //Guid g = Guid.NewGuid();
            //byte[] ga1 = g.ToByteArray();
            //byte[] ga2 = g.ToByteArray();
            //ga2[0] = 2;

            //log.Debug("GUID: " + g.ToString());
            //log.Debug("GUID: " + ShowBits(ga1));

            //log.Debug("Adler: " + Adler32.Calculate(ga1));

            //log.Debug("Adler11: " + Adler32.Calculate(ga1).Equals(Adler32.Calculate(ga1)));
            //log.Debug("Adler12: " + Adler32.Calculate(ga1).Equals(Adler32.Calculate(ga2)));

            //Application.EnableVisualStyles();
            //Application.SetCompatibleTextRenderingDefault(false);
            //Application.Run(new Form1());

            //test1();
            //testN(10);
            //testN(1000);
            //testN(50000);
            //testN(500000);

            output.WriteLine("Type anything to exit");
            input.ReadLine();
        }

        public String ShowBits(byte[] ba)
        {
            Contract.Requires(ba != null);

            System.Text.StringBuilder sb = new System.Text.StringBuilder();
            foreach (byte b in ba)
            {
                if (sb.Length != 0)
                {
                    sb.Append("-");
                }
                sb.Append(ShowBits(b));
            }
            return sb.ToString();
        }
        public String ShowBits(byte b)
        {
            byte temp = b;
            System.Text.StringBuilder sb = new System.Text.StringBuilder();
            for (int i = 0; i < 8; i++)
            {
                sb.Insert(0, (temp & 0x01) != 0 ? "1" : "0");

                temp = (byte)(temp >> 1);
            }
            return sb.ToString();
        }

        

        private void test1()
        {
            ReedSolomonNet.ReedSolomon RSEncoder = new ReedSolomonNet.ReedSolomon(4);

            doBothWays(RSEncoder, sampleText);
        }
        private void testN(int num)
        {
            Contract.Requires(num > 1);

            ReedSolomonNet.ReedSolomon RSEncoder = new ReedSolomonNet.ReedSolomon(4);
            System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
            sw.Start();
            for (int i = 0; i < num; i++)
            {
                doBothWays(RSEncoder, sampleText);
            }
            sw.Stop();
            output.WriteLine("time taken for " + num + " dual encodings: " + sw.ElapsedMilliseconds + "ms.  " + Math.Round(((double)sw.ElapsedMilliseconds / (double)num), 4));

            sw.Reset();
            sw.Start();
            for (int i = 0; i < num; i++)
            {
                doForwards(RSEncoder, sampleText, temp);
            }
            sw.Stop();
            output.WriteLine("time taken for " + num + " forwards encodings: " + sw.ElapsedMilliseconds + "ms.  " + Math.Round(((double)sw.ElapsedMilliseconds / (double)num), 4));
            byte[] encoded = new byte[sampleText.Length + 4];

            ArrayCopy(temp, encoded, encoded.Length);
            //encoded[17] = 0;
            //encoded[19] = 0;
            //encoded[20] = 0;
            //encoded[21] = 0;

            sw.Reset();
            sw.Start();
            for (int i = 0; i < num; i++)
            {
                Contract.Assume(encoded.Length > RSEncoder.NumberOfParityBytes);
                doBackwards(RSEncoder, encoded);
            }
            sw.Stop();
            output.WriteLine("time taken for " + num + " backwards encodings: " + sw.ElapsedMilliseconds + "ms.  " + Math.Round(((double)sw.ElapsedMilliseconds / (double)num), 4));

        }
        static byte[] temp = new byte[256];

        [ContractVerification(false)]
        private void ArrayCopy(Array source, Array dest, int length)
        {
            Array.Copy(source, dest, length);
        }

        private String doBothWays(ReedSolomonNet.ReedSolomon RSEncoder, String input)
        {
            Contract.Requires(RSEncoder != null);
            Contract.Requires(input != null && input.Length > 4);

            String result = null;
            try
            {
                byte[] msg = System.Text.Encoding.UTF8.GetBytes(input);
                Contract.Assume(msg != null && msg.Length > 4);

                byte[] encoded = RSEncoder.Encode(msg);

                encoded[0] = 0;
                encoded[1] = 0;
                encoded[2] = 0;
                encoded[3] = 0;

                byte[] decoded = RSEncoder.Decode(encoded, new List<int> { 0, 1, 2, 3 });

                result = tryParse(decoded);
            }
            catch (Exception e)
            { log.Error(e, "unhandled exception"); }
            if (!result.Equals(input))
            {
                throw new Exception("Encoding Failed");
            }
            return result;
        }
        private byte[] doForwards(ReedSolomonNet.ReedSolomon RSEncoder, String input, byte[] dest)
        {
            Contract.Requires(RSEncoder != null);
            Contract.Requires(input != null);
            Contract.Requires(dest != null);

            try
            {
                byte[] msg = System.Text.Encoding.UTF8.GetBytes(input);
                Contract.Assume(msg.Length > 0);
                Contract.Assume(dest.Length >= msg.Length + RSEncoder.NumberOfParityBytes);
                Contract.Assume(msg.Length + RSEncoder.NumberOfParityBytes <= ReedSolomonNet.ReedSolomon.MaxLength);
                RSEncoder.Encode(msg, dest);
            }
            catch (Exception e)
            { log.Error(e, "unhandled exception"); }
            return dest;
        }
        private String doBackwards(ReedSolomonNet.ReedSolomon RSEncoder, byte[] data)
        {
            Contract.Requires(RSEncoder != null);
            Contract.Requires(data != null);
            Contract.Requires(data.Length > RSEncoder.NumberOfParityBytes);

            String result = null;
            try
            {
                byte[] decoded = RSEncoder.Decode(data, new List<int> { 17, 19, 20, 21 });

                result = tryParse(decoded);
            }
            catch (Exception e)
            { log.Error(e, "unhandled exception"); }
            return result;
        }

        private String tryParse(byte[] text)
        {
            Contract.Requires(text != null);

            String result = "???";
            try
            {
                byte[] copy = new byte[text.Length];
                for (int i = 0; i < text.Length; i++)
                {
                    copy[i] = text[i] != 0 ? text[i] : (byte)32;
                }

                result = System.Text.Encoding.UTF8.GetString(copy);
            }
            catch { }
            return result;
        }
    }
}
